home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Skunkware 5
/
Skunkware 5.iso
/
src
/
Games
/
xmris
/
xmris.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-06-06
|
19KB
|
798 lines
/*{{{ (C) 1992 Nathan Sidwell*/
/*****************************************************************************
X M R I S V1.01
---------------
(C) 1992 Nathan Sidwell
This program is copyright (C) 1992 Nathan Sidwell. This software and documentation
is in the public domain. Permission is granted to distribute and compile
verbatim copies of this software for non-commercial, non-profit use,
without fee. The software may be modified, provided that both the above copyright
notice and this permission notice appear.
No guarantee is given as to the robustness or suitability of this
software for your computer.
Nathan Sidwell INMOS UK | | nathan@inmos.co.uk DoD#0390
*****************************************************************************/
/*}}}*/
#include "xmris.h"
#include <time.h>
/*{{{ prototypes*/
static void age_scores PROTOARGLIST((void));
static void parse_args PROTOARGLIST((int, char **));
/*}}}*/
/*{{{ void add_score(increment, x, y)*/
extern void add_score FUNCARGLIST((points, x, y))
int points FUNCARGSEP
int x FUNCARGSEP
int y FUNCARGTERM
/*
* adds the given score (which may be zero)
* and displays it at the top of the screen
* if the coordinate > 0 then add the score into the onboard list
*/
{
player.score += points;
/*{{{ text score*/
{
unsigned length;
char text[10];
int ascent, descent;
int direction;
XCharStruct chars;
int x, y;
length = itoa(text, player.score, 0);
XQueryTextExtents(display.display, font.font, text, length,
&direction, &ascent, &descent, &chars);
x = BORDER_LEFT + (CELL_WIDTH + GAP_WIDTH) * 4 -
CELL_WIDTH / 2 - chars.width;
y = (CELL_HEIGHT - ascent - descent) / 2 + ascent +
BORDER_TOP - CELL_HEIGHT;
XDrawImageString(display.display, display.back, GCN(GC_TEXT),
x, y, text, length);
add_background(x, y - ascent, chars.width, ascent + descent);
}
/*}}}*/
/*{{{ board score?*/
if(y)
{
unsigned length;
char text[10];
int i;
SCORE *sptr;
SPRITE *dptr;
dptr = &sprites[SPRITE_DIGITS];
length = itoa(text, points, 0);
/*{{{ remove oldest score?*/
if(update.score.scores == BOARD_SCORES)
{
add_background(update.score.list[0].place.x,
update.score.list[0].place.y,
DIGIT_WIDTH * 4, DIGIT_HEIGHT);
update.score.scores--;
memmove(&update.score.list[0], &update.score.list[1],
sizeof(SCORE) * update.score.scores);
}
/*}}}*/
sptr = &update.score.list[update.score.scores++];
sptr->count = SCORE_SHOW;
sptr->place.x = x - DIGIT_WIDTH * 2;
sptr->place.y = y - DIGIT_HEIGHT / 2;
/*{{{ centering*/
if(length != 4)
{
x = (4 - length) * (DIGIT_WIDTH / 2);
XCopyArea(display.display, dptr->image, sptr->image, GCN(GC_COPY),
10 * DIGIT_WIDTH, 0, x, DIGIT_HEIGHT, 0, 0);
XCopyArea(display.display, dptr->image, sptr->image, GCN(GC_COPY),
10 * DIGIT_WIDTH, 0, x, DIGIT_HEIGHT, 4 * DIGIT_WIDTH - x, 0);
XCopyArea(display.display, dptr->mask, sptr->mask, GCN(GC_COPY),
10 * DIGIT_WIDTH, 0, x, DIGIT_HEIGHT, 0, 0);
XCopyArea(display.display, dptr->mask, sptr->mask, GCN(GC_COPY),
10 * DIGIT_WIDTH, 0, x, DIGIT_HEIGHT, 4 * DIGIT_WIDTH - x, 0);
}
else
x = 0;
/*}}}*/
for(i = 0; i < length; i++, x += DIGIT_WIDTH)
{
XCopyArea(display.display, dptr->image, sptr->image, GCN(GC_COPY),
(text[i] - '0') * DIGIT_WIDTH, 0, DIGIT_WIDTH, DIGIT_HEIGHT,
x, 0);
XCopyArea(display.display, dptr->mask, sptr->mask, GCN(GC_COPY),
(text[i] - '0') * DIGIT_WIDTH, 0, DIGIT_WIDTH, DIGIT_HEIGHT,
x, 0);
}
}
/*}}}*/
return;
}
/*}}}*/
/*{{{ void age_scores()*/
static void age_scores FUNCARGVOID
/*
* ages the onboard scores, and removes the old ones
*/
{
SCORE *sptr;
int i;
for(sptr = update.score.list, i = update.score.scores; i--; sptr++)
if(!sptr->count--)
{
Pixmap image, mask;
add_background(sptr->place.x, sptr->place.y,
DIGIT_WIDTH * 4, DIGIT_HEIGHT);
mask = sptr->mask;
image = sptr->image;
memmove(sptr, sptr + 1, i * sizeof(SCORE));
sptr[i].mask = mask;
sptr[i].image = image;
update.score.scores--;
sptr--;
}
return;
}
/*}}}*/
/*{{{ void calc_distances()*/
extern void calc_distances FUNCARGVOID
/*
* sets the distances from each cell to the player
* this is so the monsters have non-local knowlegde
* increment the non-zero cells
* this proceeds as a sort of flood fill operation, starting
* from the player's cell and moving outwards
*/
{
CELL **aptr, **sptr;
CELL *list[2][FLOOD_FILL];
CELL *cptr;
int toggle;
int x, y;
int count;
global.broken = 0;
for(y = CELLS_DOWN; y--;)
{
cptr = BOARDCELL(0, y);
for(x = CELLS_ACROSS; x--; cptr++)
cptr->distance = cptr->visit ? 0 : 255;
}
toggle = 0;
cptr = BOARDCELL(monster.list[0].cell.x, monster.list[0].cell.y);
cptr->distance = count = 1;
list[0][0] = cptr;
list[0][1] = NULL;
while(list[toggle][0])
{
sptr = list[toggle];
toggle = !toggle;
aptr = list[toggle];
count++;
while(cptr = *sptr++)
{
CELL *tptr;
/*{{{ go up?*/
if(cptr->depths[0])
{
tptr = cptr - CELL_STRIDE;
if(tptr->visit && !tptr->distance)
{
tptr->distance = count;
*aptr++ = tptr;
}
}
/*}}}*/
/*{{{ go down?*/
if(cptr->depths[1])
{
tptr = cptr + CELL_STRIDE;
if(tptr->visit && !tptr->distance)
{
tptr->distance = count;
*aptr++ = tptr;
}
}
/*}}}*/
/*{{{ go left?*/
if(cptr->depths[2])
{
tptr = cptr - 1;
if(tptr->visit && !tptr->distance)
{
tptr->distance = count;
*aptr++ = tptr;
}
}
/*}}}*/
/*{{{ go right?*/
if(cptr->depths[3])
{
tptr = cptr + 1;
if(tptr->visit && !tptr->distance)
{
tptr->distance = count;
*aptr++ = tptr;
}
}
/*}}}*/
assert(aptr - list[toggle] < FLOOD_FILL);
}
*aptr = NULL;
}
return;
}
/*}}}*/
/*{{{ void fatal_error(text, ...)*/
extern void fatal_error FUNCARGLIST((text VARARG))
char const *text FUNCARGSEP
FUNCVARARG
{
va_list args;
VARARGSET(args, text);
vfprintf(stderr, text, args);
fputc('\n', stderr);
release_resources();
exit(1);
}
/*}}}*/
/*{{{ int itoa(text, n, width)*/
extern int itoa FUNCARGLIST((text, number, digits))
char *text FUNCARGSEP /* output text (include 0) */
int number FUNCARGSEP /* number to convert */
int digits FUNCARGTERM /* field width to convert into */
/*
* formats an integer to a string
* in the specified number of digits
* pads leading zeros to ' '
* returns the number of characters used
*/
{
char reverse[10];
int l, length;
l = 0;
do
{
reverse[l++] = number % 10 + '0';
number /= 10;
}
while(number);
if(!digits)
length = 0;
else if(l < digits)
{
length = digits - l;
memset(text, ' ', length);
}
else
{
length = 0;
l = digits;
}
while(l)
text[length++] = reverse[--l];
text[length] = 0;
return length;
}
/*}}}*/
/*{{{ int main(argc, argv)*/
extern int main FUNCARGLIST((argc, argv))
int argc FUNCARGSEP
char **argv FUNCARGTERM
{
#ifndef __STDC__
sprintf(title_text[1], "%s %s", XMRISVERSION, DATE);
#endif
/*{{{ set defaults*/
{
argv[argc] = NULL;
display.name = NULL;
font.name = FONT_NAME;
flags.gender = GAME_GENDER;
}
/*}}}*/
parse_args(argc, argv);
/*{{{ help?*/
if(flags.help)
{
ARG const *aptr;
char const *ptr;
if(!*argv)
ptr = game_name;
else
{
ptr = *argv;
for(ptr += strlen(ptr) - 1; ptr != *argv; ptr--)
if(ptr[-1] == '/')
break;
}
fprintf(stderr, "%s [option ...]\n", ptr);
fprintf(stderr, "%s %s\n", game_name, title_text[1]);
fprintf(stderr, "%s\n", title_text[0]);
for(aptr = args; aptr->arg; aptr++)
fprintf(stderr, " %-8s %s\n", aptr->arg, aptr->help);
return 0;
}
/*}}}*/
create_resources(argc, argv);
timer_open();
XMapWindow(display.display, display.window);
XSelectInput(display.display, display.window, display.event_mask |
KeyPressMask | ButtonPressMask | KeyReleaseMask | PointerMotionMask);
while(!demo_mode())
/*{{{ game*/
{
extra.got = 0;
extra.select = 0;
extra.escape = 0;
extra.score = 0;
create_xtra_monster(0);
player.lives = START_LIVES;
player.score = 0;
player.screen = 0;
player.pressed = 0;
while(player.lives)
{
new_board();
zoom_board();
refresh_window();
history.prize <<= 1;
history.ending <<= 2;
timer_start(FRAME_RATE);
do
{
int count;
count = SCORE_SHOW;
/*{{{ initialize stuff*/
{
player.mouse.x = PLAYER_START_X;
player.mouse.y = PLAYER_START_Y;
player.mouse_dir = player.next_dir = 0;
global.state = 0;
monster.monsters = 0;
monster.delay = 0;
monster.den = monster.normals;
monster.drones = 0;
spawn_monster(4, 2, 2, PLAYER_START_X, PLAYER_START_Y, 0, 0);
monster.list[0].stop = 1;
player.pressed = 0;
player.ball.state = 8;
player.ball.count = 0;
player.old_ball.state = 0;
}
/*}}}*/
calc_distances();
draw_center(SPRITE_DEN);
show_updates();
player.ball.state = 0;
/*{{{ game loop*/
while(count)
{
age_scores();
if(process_xevents(1))
{
player.lives = 1;
monster.list[0].shot = 1;
global.state = 4;
count = 1;
}
if(global.state != 4)
move_player();
move_monsters();
bounce_ball();
move_apples();
if(!global.state)
/*{{{ monster escape?*/
{
if(!monster.delay && random() < DEN_ESCAPE_PROB)
monster.delay = DEN_ESCAPE_DELAY * DEN_ESCAPE_FLASH + 1;
if(monster.delay)
{
monster.delay--;
if(!(monster.delay % DEN_ESCAPE_FLASH))
draw_center(monster.delay / DEN_ESCAPE_FLASH &
1 ? SPRITE_NORMAL + 4 : SPRITE_DEN);
if(!monster.delay)
{
MONSTER *mptr;
mptr = spawn_monster(0, 2, 2, DEN_X, DEN_Y, 0, 0);
monster.den--;
if(!monster.den)
{
global.state++;
draw_center(SPRITE_PRIZE_BASE +
(player.screen - 1) % SPRITE_PRIZES);
}
}
}
}
/*}}}*/
else if(global.state == 2 &&
!monster.den && !monster.drones)
global.state = 3;
fall_monsters();
/*{{{ ending?*/
if(global.state == 4)
{
if(update.score.scores || player.ball.state)
count++;
else
{
APPLE *aptr;
int i;
for(aptr = apple.list, i = apple.apples; i--; aptr++)
if(aptr->state)
{
count++;
break;
}
}
count--;
if(player.ball.state == 1)
{
player.ball.state = 2;
player.ball.count = 0;
}
else if(player.ball.state == 3)
player.ball.state = 4;
}
/*}}}*/
/*{{{ extra stuff*/
if(!extra.escape)
{
unsigned temp;
if(random() < NEXT_XTRA_PROB)
new_xtra();
temp = player.score / 5000;
if(temp != extra.score)
{
extra.score = temp;
extra_escape();
}
}
else
extra.score = player.score / 5000;
/*}}}*/
if(!global.cherries || !monster.normals ||
monster.list[0].shot || extra.got == 0x1F)
global.state = 4;
if(global.broken)
calc_distances();
show_updates();
timer_wait();
}
/*}}}*/
if(extra.got == 0x1F)
/*{{{ extra life*/
{
extra_life();
extra.got = 0;
create_xtra_monster(extra.select);
monster.list[0].shot = 0;
history.ending |= 2;
}
/*}}}*/
else if(!monster.normals)
{
monster.list[0].shot = 0;
history.ending |= 1;
}
else if(!global.cherries)
monster.list[0].shot = 0;
else if(monster.list[0].shot)
/*{{{ die*/
{
unsigned i;
unsigned base, offset;
MONSTER *mptr;
player.ball.count = 8;
i = monster.list[0].face;
if(i >= 6)
i = 2 + (i & 1);
i = SPRITE_PLAYER + i * MONSTER_IMAGES;
for(base = 8; base--;)
if(player_dies[base] == i)
break;
offset = (base + 1) & 3;
base = base & 4;
for(i = 0; i != 8; i++)
{
unsigned delay;
if(process_xevents(0))
player.lives = 1;
monster.list[0].type =
player_dies[base + ((offset + i) & 3)];
show_updates();
for(delay = DIE_DELAY; delay--;)
timer_wait();
}
for(mptr = monster.list, i = monster.monsters; i--; mptr++)
add_background(mptr->pixel.x, mptr->pixel.y,
CELL_WIDTH, CELL_HEIGHT);
monster.monsters = 0;
player.lives--;
if(player.lives)
{
XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
PIXELX(player.lives - 1, 0), PIXELY(CELLS_DOWN, 0),
CELL_WIDTH, CELL_HEIGHT);
add_background(PIXELX(player.lives - 1, 0),
PIXELY(CELLS_DOWN, 0), CELL_WIDTH, CELL_HEIGHT);
}
if(extra.escape)
{
extra.escape = 0;
draw_extra();
}
show_updates();
}
/*}}}*/
if(!monster.list[0].shot && !(player.screen % HISTORY_SHOW))
show_history();
}
while(monster.list[0].shot && player.lives);
timer_stop();
}
}
/*}}}*/
release_resources();
timer_close();
return 0;
}
/*}}}*/
/*{{{ void parse_args(argc, argv)*/
static void parse_args FUNCARGLIST((argc, argv))
int argc FUNCARGSEP
char **argv FUNCARGTERM
{
char **vptr;
static char const *names[2] = {"xmris", "xmsit"};
vptr = argv;
/*{{{ determine gender of the game*/
if(*argv)
{
unsigned length;
unsigned other;
unsigned index;
length = strlen(*argv);
for(index = 2; index--;)
{
other = strlen(&names[index][1]);
if(length >= other &&
!strcmp(&(*argv)[length - other], &names[index][1]))
flags.gender = index;
}
}
/*}}}*/
/*{{{ parse all the arguments*/
while(*++vptr)
{
while(**vptr != '-')
vptr++;
if(*vptr)
{
ARG const *aptr;
for(aptr = args; aptr->arg; aptr++)
if(!strcmp(aptr->arg, &(*vptr)[1]))
{
if(aptr->flag >= 0)
*(unsigned *)aptr->ptr = aptr->flag;
else
*(char **)aptr->ptr = *++vptr;
break;
}
}
}
/*}}}*/
game_name = names[flags.gender];
return;
}
/*}}}*/
/*{{{ int process_xevents(pausable)*/
extern int process_xevents FUNCARGLIST((pausable))
int pausable FUNCARGTERM
{
int quit;
int paused;
player.motionevent = 0;
quit = 0;
paused = 0;
/*{{{ read the events*/
while(QLength(display.display) || paused)
{
XEvent event;
XNextEvent(display.display, &event);
if(event.xany.window != display.window)
continue;
switch (event.type)
{
/*{{{ case KeyPress:*/
case KeyPress:
/*
* When a key is pressed, we check to see if it is
* a control key. If so, then we set the relevant pressed bit.
*/
{
char chr;
KeySym symbol;
XComposeStatus status;
XLookupString(&event.xkey, &chr, 1, &symbol, &status);
if(isupper(chr))
chr = tolower(chr);
if(chr == 'p' && pausable)
paused++;
else if(paused && chr == ' ')
player.motionevent = paused = 0;
else if((paused || !pausable) && chr == 'q')
{
paused = 0;
quit = 1;
}
else if(global.state > 5)
player.keyboard = player.button = 1;
else if(!paused && player.keyboard)
{
int index;
for(index = 5; index--;)
if(chr == keystrokes[index])
{
if(index == 4)
player.button = 1;
else
player.pressed |= 1 << index;
break;
}
}
break;
}
/*}}}*/
/*{{{ case KeyRelease:*/
case KeyRelease:
/*
* when a key is released, we check to see if it is
* the controlling direction key. If so, then we stop
*/
{
if(player.keyboard)
{
char chr;
KeySym symbol;
XComposeStatus status;
unsigned index;
XLookupString(&event.xkey, &chr, 1, &symbol, &status);
if(isupper(chr))
chr = tolower(chr);
for(index = 5; index--;)
if(chr == keystrokes[index])
{
if(index == 4)
player.button = 0;
else
player.pressed &= ~(1 << index);
break;
}
break;
}
}
/*}}}*/
/*{{{ case MotionNotify:*/
case MotionNotify:
/*
* for mouse motion, we save the coordinate and process the
* final one only
*/
{
player.raw_mouse.x = event.xmotion.x;
player.raw_mouse.y = event.xmotion.y;
player.motionevent = 1;
break;
}
/*}}}*/
/*{{{ case ButtonPress:*/
case ButtonPress:
if(!player.keyboard)
player.button = 1;
break;
/*}}}*/
/*{{{ case ButtonRelease:*/
case ButtonRelease:
if(!player.keyboard && player.throw)
player.button = 0;
break;
/*}}}*/
/*{{{ case Expose:*/
case Expose:
refresh_window();
break;
/*}}}*/
/*{{{ case UnmapNotify:*/
case UnmapNotify:
paused++;
break;
/*}}}*/
/*{{{ case MapNotify:*/
case MapNotify:
if(!pausable)
paused = 0;
break;
/*}}}*/
/*{{{ case PropertyNotify:*/
case PropertyNotify:
if(event.xproperty.atom == display.DEC_icon_atom)
paused++;
break;
/*}}}*/
default:
break;
}
/*{{{ started to pause?*/
if(paused == 1)
{
char const *text = "Space to continue, Q to quit";
TEXT info;
unsigned length;
paused++;
length = strlen(text);
text_size(text, length, &info);
add_background(0, PIXELY(CELLS_DOWN, 0), WINDOW_WIDTH, CELL_HEIGHT);
XFillRectangle(display.display, display.copy, GCN(GC_CLEAR),
0, PIXELY(CELLS_DOWN, 0), WINDOW_WIDTH, CELL_HEIGHT);
XDrawImageString(display.display, display.copy, GCN(GC_TEXT),
WINDOW_WIDTH / 2 - info.width / 2,
PIXELY(CELLS_DOWN, CELL_HEIGHT / 2) +
(info.ascent - info.descent) / 2, text, length);
XCopyArea(display.display, display.copy, display.window, GCN(GC_COPY),
0, PIXELY(CELLS_DOWN, 0), WINDOW_WIDTH, CELL_HEIGHT,
0, PIXELY(CELLS_DOWN, 0));
XSync(display.display, False);
}
/*}}}*/
}
/*}}}*/
return quit;
}
/*}}}*/
#if !defined(sco)
/*{{{ unsigned random()*/
extern unsigned random FUNCARGVOID
/*
* a simple random number generator
* it generates 8 new bits of number at each call
* using a 31 bit maximal length linear feedback shift register
* the taps are bits 0 and 3
*/
{
unsigned bits;
if(!seed)
seed = time(NULL);
bits = ((seed >> 3) ^ seed) & 0xFF;
seed = (seed >> 8) | (bits << 23);
return bits;
}
/*}}}*/
#endif